package com.cloud.service.bookkeeping.impl;

import com.cloud.commons.constants.ConsumeConstant;
import com.cloud.commons.utils.DateUtils;
import com.cloud.dao.bookkeeping.BookkeepingDao;
import com.cloud.dao.wxuser.WxUserDao;
import com.cloud.dao.wxusersubscribe.WxUserSubscribeDao;
import com.cloud.model.entity.assetsdetail.AssetsDetail;
import com.cloud.model.entity.bookkeeping.Bookkeeping;
import com.cloud.model.entity.wxuser.WxUser;
import com.cloud.model.entity.wxusersubscribe.WxUserSubscribe;
import com.cloud.model.view.bookkeeping.BookkeepingView;
import com.cloud.service.bookkeeping.BookkeepingService;
import com.cloud.commons.utils.CloudCopyUtils;
import com.cloud.service.assetsdetail.AssetsDetailService;
import com.cloud.dao.assetsdetail.AssetsDetailDao;
import com.cloud.service.wxtemplatemessage.WxTemplateMessageService;
import com.cloud.thread.AssetsDetailThread;
import com.cloud.thread.WxTemplateMessageThread;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.transaction.Transactional;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;

/**
* <b><code>BookkeepingImpl</code></b>
* <p/>
* Bookkeeping的具体实现
* <p/>
* <b>Creation Time:</b> 2019-10-15 17:24:19.
*
* @author ${model.author}
* @version 1.0.0
* @since cloud-spring-boot-be 1.0.0
*/
@Service
public class BookkeepingServiceImpl implements BookkeepingService{

    /**
     * The Constant LOG.
     */
    private static final Logger LOG = LoggerFactory
            .getLogger(BookkeepingServiceImpl.class);

    @Autowired
    private BookkeepingDao bookkeepingDao;

    @Autowired
    private AssetsDetailService assetsDetailService;

    @Autowired
    private WxUserDao wxUserDao;

    @Autowired
    private AssetsDetailDao assetsDetailDao;

    @Autowired
    WxTemplateMessageService wxTemplateMessageService;

    @Autowired
    private WxUserSubscribeDao wxUserSubscribeDao;

    @Override
    public BookkeepingView getEntity(long id) {
        // 获取Entity
        Bookkeeping bookkeeping = bookkeepingDao.findById(id).orElseThrow(() -> new RuntimeException("获取不到id为：" + id + "的数据记录"));
        // 复制Dao层属性到view属性
        BookkeepingView bookkeepingView = new BookkeepingView();
        CloudCopyUtils.copyBean(bookkeeping, bookkeepingView);
        return bookkeepingView;
    }

    @Override
    public long getEntitiesCount() {
        return bookkeepingDao.count();
    }

    @Override
    public List<BookkeepingView> findAll() {
        List<BookkeepingView> bookkeepingViews = new ArrayList<>();
        List<Bookkeeping> bookkeepings = bookkeepingDao.findAll();
        for (Bookkeeping bookkeeping : bookkeepings){
            BookkeepingView bookkeepingView = new BookkeepingView();
            CloudCopyUtils.copyBean(bookkeeping, bookkeepingView);
            bookkeepingViews.add(bookkeepingView);
        }
        return bookkeepingViews;
    }

    @Override
    public BookkeepingView saveEntity(BookkeepingView bookkeepingView)  {
        // 保存的业务逻辑
        Bookkeeping bookkeeping = new Bookkeeping();
        CloudCopyUtils.copyBean(bookkeepingView, bookkeeping);
        // user数据库映射传给dao进行存储
        bookkeeping.setCreateTime(System.currentTimeMillis());
        bookkeeping.setUpdateTime(System.currentTimeMillis());
        bookkeeping.setWeight(System.currentTimeMillis());
        bookkeeping.setConsumeTime(System.currentTimeMillis());
        bookkeeping.setConsumeTimeDay(DateUtils.getDayStartTime(System.currentTimeMillis()));
        bookkeeping.setIsShowTime(ConsumeConstant.ENABLED);
        if (ObjectUtils.isEmpty(bookkeeping.getRemarks())) {
            bookkeeping.setRemarks(ConsumeConstant.REMART);
        }
        bookkeeping.setEnabled(1);
        bookkeepingDao.save(bookkeeping);
        CloudCopyUtils.copyBean(bookkeeping,bookkeepingView);
        return bookkeepingView;
    }

    @Override
    @Transactional
    public void deleteEntity(long id) {
        Bookkeeping bookkeeping = bookkeepingDao.findById(id).orElseThrow(() -> new RuntimeException("获取不到id为：" + id + "的数据记录"));
        bookkeeping.setId(id);
        bookkeepingDao.delete(bookkeeping);
        // 判断is_show是否为1,1 ：将最近一条数据修改为1
        existsIsShowTime(bookkeeping);

        // 更新数据信息
        assetsDetailService.updateMonyByUserIdAndConsumeTime(bookkeeping.getUserId(),bookkeeping.getConsumeTime());
    }

    /**
     * 判断is_show是否为1,1 ：将最近一条数据修改为1
     * @param bookkeeping
     */
    private void existsIsShowTime(Bookkeeping bookkeeping) {
        if (bookkeeping.getIsShowTime().equals(ConsumeConstant.ENABLED)) {
            Long startTime = DateUtils.getDayStartTime(bookkeeping.getConsumeTime());
            Long endTime = DateUtils.getDayEndTime(bookkeeping.getConsumeTime());
            List<Bookkeeping> bookkeepings = bookkeepingDao.findByUserIdAndIsShowTimeAndConsumeTimeBetweenOrderByWeightDesc(bookkeeping.getUserId(),ConsumeConstant.DISABLED,startTime,endTime);
            if (ObjectUtils.isEmpty(bookkeepings) || bookkeepings.size() == 0) {
                return;
            }
            bookkeepingDao.updateIsShowTimeById(ConsumeConstant.ENABLED,System.currentTimeMillis(),bookkeepings.get(0).getId());
        }
    }

    @Override
    @Transactional(rollbackOn = { Exception.class })
    public void deleteEntities(String ids) {
        String[] entityIds= ids.split("\\,");
        List<Bookkeeping> bookkeepings = new ArrayList<>();
        for(String entityId : entityIds){
            Bookkeeping bookkeeping = new Bookkeeping();
            bookkeeping.setId(Long.valueOf(entityId));
            bookkeepings.add(bookkeeping);
        }
        bookkeepingDao.deleteInBatch(bookkeepings);
    }

    @Override
    public void updateEntity(BookkeepingView bookkeepingView) {
        Specification<Bookkeeping> bookkeepingSpecification = Optional.ofNullable(bookkeepingView).map( s -> {
            return new Specification<Bookkeeping>() {
                @Override
                public Predicate toPredicate(Root<Bookkeeping> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                    List<Predicate> predicates = new ArrayList<>();
                    // id
                    predicates.add(criteriaBuilder.equal(root.get("id").as(Long.class), s.getId()));

                    criteriaQuery.where(predicates.toArray(new Predicate[predicates.size()]));
                    return criteriaQuery.getRestriction();
                }
            };
        }).orElseThrow(()->new IllegalArgumentException("BookkeepingView is null"));


        // 获取原有的属性，把不变的属性覆盖
        Optional<Bookkeeping> bookkeepingOptionalBySearch = bookkeepingDao.findOne(bookkeepingSpecification);
        bookkeepingOptionalBySearch.map(bookkeepingBySearch -> {
            CloudCopyUtils.copyBeanExcludeNull(bookkeepingView,bookkeepingBySearch);
            bookkeepingBySearch.setUpdateTime(System.currentTimeMillis());
            bookkeepingDao.save(bookkeepingBySearch);
            // 更新统计表数据信息
            assetsDetailService.updateMonyByUserIdAndConsumeTime(bookkeepingBySearch.getUserId(),bookkeepingBySearch.getConsumeTime());
            return "";
        }).orElseThrow(()->new RuntimeException("无法找到id为" + bookkeepingView.getId() + "的数据记录"));
    }

    @Override
    public Page<BookkeepingView> getEntityPage(Long userId, Integer pageNumber, Integer pageSize) {
        Pageable pageable = PageRequest.of(pageNumber,pageSize);
        Page<Bookkeeping> bookkeepingPage= bookkeepingDao.findAllByUserIdAndEnabledOrderByConsumeTimeDesc(userId, ConsumeConstant.ENABLED,pageable);
        return bookkeepingPage.map(bookkeeping -> {
            BookkeepingView bookkeepingView = new BookkeepingView();
            CloudCopyUtils.copyBean(bookkeeping,bookkeepingView);
            // 统计当天总消费与日期
            this.countTotalMomeyAndNowDayTime(bookkeepingView);
            return bookkeepingView;
        });
    }

    @Override
    @Transactional
    public BookkeepingView saveBookkeepingView(BookkeepingView bookkeepingView) {
        // 吧当天的相关记账数据isShowTime更新为0
        this.updateIsShowTime(bookkeepingView.getUserId());
        // 保存记账信息
        bookkeepingView = this.saveEntity(bookkeepingView);
        // 更新本月统计表信息 (用多线程进行更新)
        assetsDetailService.updateMonyByUserIdAndConsumeTime(bookkeepingView.getUserId(),bookkeepingView.getConsumeTime());
//        AssetsDetailThread assetsDetailThread = new AssetsDetailThread(bookkeepingView.getUserId(),bookkeepingView.getConsumeTime());
//        assetsDetailThread.start();
        try {
            // 校验本月是否超出，发送模板消息 (用多线程进行发送)
            WxTemplateMessageThread wxTemplateMessageThread = new WxTemplateMessageThread(bookkeepingView);
            wxTemplateMessageThread.start();
        } catch (Exception e) {
            // 纯记录，不需要抛出
            LOG.error("发送模板消息异常：" + e.getMessage());
        } finally {
            System.out.println("主线程 - 保存记账表成功");
            return bookkeepingView;
        }
    }
    private void updateIsShowTime(Long userId) {
        Long currentStartTime = DateUtils.getCurrentDayStartTime();
        Long currentEndTime = DateUtils.getCurrentDayEndTime();
        bookkeepingDao.updateIsShowTimeByUserIdAndIsShowTimeAndConsumeTimeBetween(System.currentTimeMillis(),userId,ConsumeConstant.ENABLED,currentStartTime,currentEndTime);
    }

    private void countTotalMomeyAndNowDayTime(BookkeepingView bookkeepingView) {
        if (bookkeepingView.getIsShowTime().intValue() == 0) {
            return;
        }
        Long dayStartTime = DateUtils.getDayStartTime(bookkeepingView.getConsumeTime());
        Long dayEndTime = DateUtils.getDayEndTime(bookkeepingView.getConsumeTime());
        Long userId = bookkeepingView.getUserId();
        String formatConsumeTime = DateUtils.timeStamp2Date(String.valueOf(bookkeepingView.getConsumeTime()),ConsumeConstant.DATE_FORMAT);
        String formatConsumeTimeDay = DateUtils.getWeekOfDate(new Date(bookkeepingView.getConsumeTime()));
        Integer totalMoney = bookkeepingDao.sunByEnabledAndUserIdAndConsumeTimeBetween(ConsumeConstant.ENABLED,userId,dayStartTime,dayEndTime);
        bookkeepingView.setTotalMoney(totalMoney);
        bookkeepingView.setNowDayTime(formatConsumeTime + " " + formatConsumeTimeDay);
    }

//    public static void main(String[] args) {
//        AssetsDetailThread assetsDetailThread = new AssetsDetailThread();
//        assetsDetailThread.start();
//        AssetsDetailThread assetsDetailThread1 = new AssetsDetailThread();
//        assetsDetailThread1.start();
//    }
}
